The central processing unit (CPU) or processor lies at the heart of all modern computing systems. The processor executes instructions of a computer program and thus enables the computer to perform useful work. CPUs are prevalent in all forms of digital devices in modern life and not just in dedicated computing machines such as personal computers, laptops and PDAs. Microprocessors appear in everything from automobiles to cellular telephones to children's toys.
A problem arises in that program code which is executable by one type of processor often cannot be executed in any other type of processor, because each type of processor has its own unique Instruction Set Architecture (ISA). Hence, program code conversion has evolved to automatically convert program code written for one type of processor into code which is executable by another type of processor, or to optimise an old, inefficient piece of code into a newer, faster version for the same type of processor. That is, in both embedded and non-embedded CPUs, there are predominant ISAs for which large bodies of software already exist that could be “accelerated” for performance or “translated” to other processors that present better cost/performance benefits. One also finds dominant CPU architectures that are locked in time to their ISA and cannot evolve in performance or market reach. This problem applies at all levels of the computing industry, from stand-alone pocket-sized computing devices right through to massive networks having tens or hundreds of powerful servers.
As background information in this field of program code conversion, PCT publication WO2000/22521 entitled “Program Code Conversion”, WO2004/095264 entitled “Method and Apparatus for Performing Interpreter Optimizations during Program Code Conversion”, WO2004/097631 entitled “Improved Architecture for Generating Intermediate Representations for Program Code Conversion”, WO2005/006106 entitled “Method and Apparatus for Performing Adjustable Precision Exception Handling”, and WO2006/103395 entitled “Method and Apparatus for Precise Handling of Exceptions During Program Code Conversion”, which are all incorporated herein by reference, disclose methods and apparatus to facilitate program code conversion capabilities as may be employed in the example embodiments discussed herein.
One particular problem area concerns the handling of exception signals. An exception is a condition that changes the normal flow of control in a program. An exception signal indicates that a condition has occurred somewhere within the system that requires the attention of the processor and usually needs to be handled before processing can continue. Exceptions can be subdivided into various different types such as interrupts, faults, traps or aborts. The terminology varies between different architectures, and particular types or categories of exceptions may be unique to particular architectures.
Exception signals (often simply called “signals” or “exceptions”) may be raised by hardware or by software. Hardware exception signals include resets, interrupts, or signals from a memory management unit. As examples, exceptions may be generated by an arithmetic logic unit or floating-point unit for numerical errors such as divide-by-zero, for overflow or underflow, or for instruction decoding errors such as privileged, reserved, trap or undefined instructions. Software exceptions vary greatly across various different software programs but generally are applied to any kind of error checking which alters the normal behaviour of the program.
A signal handler is a special unit which is called upon when an exception signal occurs during the execution of a program. The signal handler then attempts to deal with whatever circumstances gave rise to the exception and, if possible, continue execution of the program. If the program does not provide a signal handler for a given signal then a default system signal handler will be called.
The most common events that trigger exception signals are when a process tries to (i) access an unmapped memory region or (ii) manipulate a memory region for which it does not have the correct permissions. Other common events that trigger exception signals are (iii) receipt of a signal sent from another process, (iv) execution of an instruction that the current process does not have the privilege level to execute, or (v) an Input/Output event in the hardware.
Some representative exception signals are described in Table 1. From the perspective of the computing system, each type of signal has a corresponding signal number which is usually an integer number #1, #2, #3, etc. Also, as shown in the table, it is common for each signal to have a memorable symbolic name.
TABLE 1Example Exception SignalsSigNumSignalDescription#1SIGHUP“Hangup” - commonly used to indicate to a process that its configurationhas changed, and that it should re-read its config file.#2SIGINT“Interrupt” - usually means Ctrl-C has been pressed by the user.#3SIGILL“Illegal Instruction” - the processor generates this when an invalidinstruction opcode is encountered.#4SIGTRAP“Breakpoint” - often used by debuggers.#5SIGBUS“Bus Error” - usually generated by the processor to indicate an invalidmemory access. This is usually an access to an unallocated or unalignedmemory address.#6SIGSEGV“Segmentation Violation” - generated by the processor when a user processhas tried to do something not permissible in user mode. For example, tryingto execute a privileged instruction, or trying to write to part of the kernelmemory would both raise this signal.#7SIGALRM“Alarm Clock” - a process can make an alarm( ) system call, which requeststhe delivery of this signal n seconds later.#8SIGTERM“Terminate” - polite request for a program to think about exiting, if it's nottoo inconvenient.#9SIGQUIT“Quit” - Firm request for a program to exit, now please!#10SIGKILL“Die” - immediately terminates the process. This signal cannot beintercepted by a signal handler.
Exception signals can come from two sources: (1) directly from an executing program or (2) from the operating system or another process. Some exception signals are generated as a direct result of an instruction executed by the program. For example, if a program executes an illegal opcode, then SIGILL is raised. Similarly, if the program attempts an illegal memory access then SIGSEGV is raised. These are referred to as in-band signals. Exception signals can also be generated externally, either by the operating system or by another process. SIGHUP and SIGALRM are examples of these. These externally generated exception signals are called out-of-band signals.
From a program's point of view, an exception signal can occur at any time. When an exception signal occurs, the operating system interrupts the execution of the signalled program and invokes a signal handler. The operating system usually defines default signal handlers for all exceptions which receive the exception signals by default and either take predefined actions or simply ignore the signal. However, the operating system maintains a process-specific handling table which maps each type of signal to a registered signal handler. For example, in Unix, a program can override a default signal handler by invoking a sigaction( ) system call. Sigaction( ) allows the program to specify what action the operating system should take when a particular exception signal is received. The action can be: (1) ignore the exception signal; (2) call the default signal handler; or (3) call a specialized signal handler function, whose address is provided by the program. Other options that can be specified when making the sigaction( ) call include which other signals are blocked during execution of a signal handler, in much the same way as a CPU can mask certain interrupts.
A Unix signal handler is typically provided with one of two prototypes. The first signal handler prototype is “void sigHandler(int sigNum).” The first argument is the number of the exception signal, so that one function can be registered to handle multiple signals. A program can request that more information be provided to the signal handler by calling sigaction( ) with the SA_SIGINFO flag. In this case, the Unix signal handler prototype becomes “void sigHandler(int sigNum, siginfo_t sigInfo, void *context).”
The second parameter (“siginfo”) is a structure which contains information about the signal, including some indication of what caused the signal and where it came from. For example, in the case of a SIGILL signal, the siginfo structure contains the address of the illegal instruction. This data can be essential to allow the process to handle the signal properly. The third parameter (“context”) provides access to the processor state (including all registers) at the time the signal was raised. Again, this data can be essential to allow correct handling of a signal. The signal handler is allowed to modify this context and, when execution is resumed, the registers are then restored to the values of the modified context.
Where the original program code (here called “subject code”) has been written according to a particular type of processor (the “subject processor”), then that subject code requires a particular type of execution environment and must be supported by an appropriate mechanism for the handling of exception signals. However, under program code conversion, the subject code is instead converted into target code and is executed on a target computing system. When an exception arises, there is now a difficulty in providing an appropriate mechanism to handle the exception signals.
In the field of program code conversion, it is apparent that when an instruction is executed on a target processor and causes an exception signal to be reported, this instruction generally will not fulfil the conditions for reporting an exception to a signal handler written in subject code. Instructions are almost always performed on the target processor in a different order to the order of instructions in the corresponding block of subject code, firstly due to the differences between the instruction set of the subject processor for which the subject code was written and the target processor on which the target code is run, and secondly because of the optimisations that typically occur during program code conversion. Hence, it is relatively easy to provide a target state to a signal handler, but it is difficult and costly to obtain an accurate subject state representing an equivalent point in execution of the original subject code.
A further complication arises in that many real-world subject programs comprise multiple units of subject code. A single subject program may include a main subject executable and also make use of a number of subject libraries, some of which may be proprietary and some of which typically will be provided as part of the subject operating system. As the subject program runs, control flow passes between these different units of subject code as function calls are made to certain functions within the subject libraries.
PCT application WO2005/008478 (also published as US2005/0015781A), which is incorporated herein by reference, discloses a native binding technique for inserting calls to well-defined portions of native code, particularly native functions, during translation of subject code to target code, such that function calls in the subject program to subject code functions are replaced in target code with calls to native equivalents of the same functions. This avoids the overhead of translating the subject versions of those functions into target code. In addition, the native version may be a much more efficient implementation of the same functionality, as the native version can better exploit architectural features of the target architecture which may not be available on the subject architecture.
The native binding technique is very useful, because the native code is written or compiled directly for the target processor and hence runs very efficiently on the target processor. However, the native code—such as a generic library function—often does not maintain any overheads in the translation process, such as the work of maintaining an accurate subject state. That is, the native code is fast but generally does not perform useful tasks associated with the translation process that would normally be undertaken by the target code produced by the translator, such as maintaining an accurate subject state for reporting to a subject exception handler.
Further, the inventors have identified difficulties in relation to resuming execution of the subject program after the exception has been handled, and particularly where it is desired to resume execution of the native code.